package com.baidu.sggc.service;

import com.baidu.sggc.core.common.utils.MenuPathUtil;
import com.baidu.sggc.core.service.MailService;
import com.baidu.sggc.entity.AuditLog;
import com.baidu.sggc.entity.OperateType;
import com.baidu.sggc.entity.SyncFailureLog;
import com.baidu.sggc.entity.SyncOffset;
import com.baidu.sggc.mapper.AuditLogMapper;
import com.baidu.sggc.mapper.SyncFailureLogMapper;
import com.baidu.sggc.mapper.SyncOffsetMapper;
import com.baidu.sggc.core.common.utils.AuditDescUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutorService;

@Slf4j
@Service
@RequiredArgsConstructor
public class AuditLogSyncService {

    private static final String OFFSET_KEY = "audit_log_to_event";

    @Autowired
    private SyncOffsetMapper syncOffsetMapper;

    @Autowired
    private SyncFailureLogMapper syncFailureLogMapper;

    @Autowired
    private AuditLogMapper auditLogMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${audit.sync.remote-url}")
    private String remoteUrl;

    @Autowired
    private MailService mailService;

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private final ExecutorService mailExecutor; // 自动注入 ThreadPoolConfig 里的 Bean
    /**
     * 主同步方法
     */
    @Transactional
    public void syncAuditLogs() {
        SyncOffset offset = syncOffsetMapper.selectById(OFFSET_KEY);
        long lastOffset = (offset != null) ? offset.getOffset() : 0L;

        List<AuditLog> newLogs = auditLogMapper.selectNewLogs(lastOffset, 100);
        log.info("【位点信息】当前offset={}, 拉取新日志数量={}", lastOffset, newLogs.size());

        if (newLogs.isEmpty()) {
            log.info("【审计日志同步】当前无新日志，结束同步");
            return;
        }

        log.info("【拉取新日志】日志范围：{} - {}", newLogs.get(0).getPk(), newLogs.get(newLogs.size() - 1).getPk());

        long startTime = System.currentTimeMillis();
        int success = 0;
        int fail = 0;

        for (AuditLog auditLog : newLogs) {
            Long auditLogId = auditLog.getPk();
            String operation = auditLog.getOperation();
            String menuPath = MenuPathUtil.formatMenuPath(auditLog.getMenuPath());

            // ======= 过滤逻辑 =======
            if ((menuPath == null || menuPath.trim().isEmpty()) && !"NoPermission".equals(operation)) {
                log.warn("【过滤】日志ID={}，menuPath为空，且非 NoPermission，跳过并推进 offset", auditLogId);
                updateSyncOffset(auditLogId);
                continue;
            }
            if (auditLog.getExtendData() == null || auditLog.getExtendData().trim().isEmpty()) {
                log.warn("【过滤】日志ID={}，extendData为空，跳过并推进 offset", auditLogId);
                updateSyncOffset(auditLogId);
                continue;
            }
            if ("login".equalsIgnoreCase(operation) || "logout".equalsIgnoreCase(operation)) {
                log.info("【过滤】日志ID={}，login/logout 操作，跳过并推进 offset", auditLogId);
                updateSyncOffset(auditLogId);
                continue;
            }

            // 查询类处理
            if (operation != null) {
                String opLower = operation.toLowerCase();
                if ("custom_listQuery".equals(opLower) || "custom_detailsQuery".equals(opLower)) {
                    String newOp = "custom_listQuery".equals(opLower) ? "listQuery" : "detailsQuery";
                    auditLog.setOperation(newOp);
                    operation = newOp;
                } else if (("listQuery".equals(opLower) || "detailsQuery".equals(opLower))
                        && !"custom-portal".equalsIgnoreCase(auditLog.getAppName())) {
                    log.info("【过滤】日志ID={}，普通查询类且非 custom-portal，跳过", auditLogId);
                    updateSyncOffset(auditLogId);
                    continue;
                }
            }

            try {
                // 如果是越权访问，异步发邮件
                if ("NoPermission".equalsIgnoreCase(operation)) {
                    sendIllegalAccessMailAsync(auditLog);
                }

                Map<String, Object> requestBody = buildRequestBody(auditLog);

                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);

                ResponseEntity<String> response = restTemplate.postForEntity(remoteUrl, requestEntity, String.class);

                if (response.getStatusCode().is2xxSuccessful()) {
                    updateSyncOffset(auditLogId);
                    success++;
                } else {
                    recordFailure(auditLogId, "接口返回非成功状态：" + response.getStatusCodeValue());
                    fail++;
                }

            } catch (Exception e) {
                log.error("【同步异常】日志ID={}，异常={}", auditLogId, e.getMessage(), e);
                recordFailure(auditLogId, "同步异常：" + e.getMessage());
                fail++;
            }
        }

        long cost = System.currentTimeMillis() - startTime;
        log.info("【审计日志同步结束】共处理={}，成功={}，失败={}，耗时={}ms", newLogs.size(), success, fail, cost);
    }

    /**
     * 异步发送越权通知邮件
     */
    public void sendIllegalAccessMailAsync(AuditLog audit) {
        mailExecutor.submit(() -> {
            try {
                mailService.sendSimpleMail("ls3@isc.com", "越权通知", AuditDescUtil.buildDesc(audit));
            } catch (Exception e) {
                log.error("【越权通知邮件发送失败】日志ID={}，异常={}", audit.getPk(), e.getMessage(), e);
            }
        });
    }

    private void updateSyncOffset(Long pk) {
        SyncOffset offset = new SyncOffset();
        offset.setId(OFFSET_KEY);
        offset.setOffset(pk);
        offset.setUpdateTime(LocalDateTime.now());
        syncOffsetMapper.insertOrUpdate(offset);
    }

    /**
     * 记录失败日志
     */
    private void recordFailure(Long pk, String errorMsg) {
        SyncFailureLog failLog = new SyncFailureLog();
        failLog.setAuditLogPk(pk);
        failLog.setErrorMessage(errorMsg);
        failLog.setFailTime(new Date());
        failLog.setRetryCount(0);
        syncFailureLogMapper.insert(failLog);
    }

    /**
     * 纯数据构造方法（无副作用）
     */
    private Map<String, Object> buildRequestBody(AuditLog audit) {
        Map<String, Object> map = new HashMap<>();
        map.put("userId", audit.getUserId());

        String rawUserName = audit.getUserName();
        map.put("userName", (rawUserName != null && rawUserName.startsWith("user_"))
                ? rawUserName.substring(5) : rawUserName);

        String operation = audit.getOperation();
        if ("NoPermission".equalsIgnoreCase(operation)) {
            map.put("eventId", "SYS");
            map.put("operateId", "SYS_ILLEGAL_ACCESS");
        } else {
            map.put("eventId", "BIZ");
            map.put("operateId", operation == null ? null :
                    Optional.ofNullable(OperateType.getByEnName(operation))
                            .map(OperateType::getZhName)
                            .orElse(operation));
        }

        map.put("domain", MenuPathUtil.formatMenuPath(audit.getMenuPath()));
        map.put("userIp", audit.getAccessAddress());

        JsonNode extendData = parseExtendData(audit.getExtendData());
        map.put("sessionId", getStringValue(extendData, "unique_identification", ""));
        map.put("appId", "大模型工具链平台");
        map.put("desc", AuditDescUtil.buildDesc(audit));
        map.put("isSuccess", isSuccess(audit.getOperateState()));
        // 先判断是否为NoPermission，是则设置为2
        if ("NoPermission".equalsIgnoreCase(operation)) {
            map.put("exceptionLevel", "2");
        } else {
            // 否则根据操作状态判断是0还是1
            map.put("exceptionLevel", "success".equalsIgnoreCase(audit.getOperateState()) ? "0" : "1");
        }
        return map;
    }

    private boolean isSuccess(String operateState) {
        return "success".equalsIgnoreCase(
                operateState != null ? operateState.trim() : ""
        );
    }

    private static JsonNode parseExtendData(String extendDataJson) {
        try {
            return OBJECT_MAPPER.readTree(extendDataJson);
        } catch (Exception e) {
            return null;
        }
    }

    private static String getStringValue(JsonNode node, String fieldName, String defaultValue) {
        return (node != null && node.has(fieldName)) ? node.get(fieldName).asText() : defaultValue;
    }
}
